// SobelEdgeDetector.cpp : implementation file
//

#include "stdafx.h"
#include "SobelEdgeDetector.h"
#include "../Color Filters/GrayscaleRMY.h"
#include "../../ImageCores/ImageEx.h"

int CSobelEdgeDetector::xKernel[3][3] = 
{
	{-1,  0,  1},
	{-2,  0,  2},
	{-1,  0,  1}
};

int CSobelEdgeDetector::yKernel[3][3] =
{
	{ 1,  2,  1},
	{ 0,  0,  0},
	{-1, -2, -1}
};

// CSobelEdgeDetector

CSobelEdgeDetector::CSobelEdgeDetector()
{
}

CSobelEdgeDetector::~CSobelEdgeDetector()
{
}

BOOL CSobelEdgeDetector::Apply(CxImage& srcImage, CxImage& destImage)
{
	BOOL bSuccess = FALSE;
	int width = srcImage.GetWidth();
	int height = srcImage.GetHeight();

	// convert input to grayscale if it's not yet
	CxImage *grayImage;
	if (!srcImage.IsGrayScaled())
	{
		grayImage = new CImageEx();
		grayImage->Create(srcImage.GetSize(), srcImage.GetBPP());
		IImageFilter *filter = new CGrayscaleRMY();
		bSuccess = filter->Apply(srcImage, *grayImage);
		delete filter;
		if (!bSuccess)
			return FALSE;
	}
	else
		grayImage = &srcImage;

	// apply the sobel edge detector on the image
	bSuccess = grayImage->ResetIteration() && destImage.ResetIteration();
	if (bSuccess)
	{
		int gx, gy, g, maxValue = 0, x, y;
		RGBQUAD value;
		while (grayImage->GetNextPixelColor(value))
		{
			x = grayImage->GetCurrentX();
			y = grayImage->GetCurrentY();

			// if the current point is around the edge, skip it.
			if (x == 0 || y == 0 || x == width - 1 || y == height - 1)
			{
				// move the dest image one step
				destImage.SetNextPixelColor(value);
				continue;
			}

			gx = value.rgbRed * xKernel[1][1];
			gy = value.rgbRed * yKernel[1][1];
			// for each kernel row
			for (int i = 0; i < 3; i++) {
				// for each kernel col
				for (int j = 0; j < 3; j++)
				{
					if (!(i == 1 && j == 1))
					{
						grayImage->GetPixelinBuffer(x + i - 1, y + j - 1, value);
						gx += value.rgbRed * xKernel[i][j];
						gy += value.rgbRed * yKernel[i][j];
					}
				}
			}
			// get gradient value
			g = min(abs(gx) + abs(gy), 255);	// approximation
			if (g > maxValue) maxValue = g;

			// set the destination image pixel
			value.rgbRed = value.rgbGreen = value.rgbBlue = g;
			
			destImage.SetNextPixelColor(value);
		}

		// start to do the scaling to enhance the intensity
		if (maxValue != 255)
		{
			double factor = 255 / (double)maxValue;
			destImage.ResetIteration();
			while(destImage.GetNextPixelColor(value))
			{
				value.rgbRed = value.rgbGreen = 
					value.rgbBlue = value.rgbBlue * factor;
				destImage.SetCurrentPixel(value);
			}
		}
		bSuccess = TRUE;
	}

	destImage.SetGrayScaled(TRUE);

	// clear the grayscale image
	if (!srcImage.IsGrayScaled())
		delete grayImage;

	return bSuccess;
}

// CSobelEdgeDetector member functions
